//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "ddk.h"
#include "Jffs2File.h"
#define _NO_CJFFS2FILE_CLASSOBJECT_
#include "_CJffs2File.cpp"

#include <stdlib.h>
#include "jffs2_misc.h"
#include "debug.h"

/*
 * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
 * interval there were 8 regular years and 2 leap years.
 */
#define    SECONDSTO1980    (((8 * 365) + (2 * 366)) * (24 * 60 * 60))

#define SIZE_MAX LONG_MAX

CJffs2File::~CJffs2File()
{
    D2(printf("In ~CJffs2File.\n"));
    assert(m_file.f_data);
    IoOperator::jffs2_fo_close(&m_file);
}

ECode CJffs2File::Read(
                /* [in] */ UInt32 offset,
                /* [in] */ UInt32 toRead,
                /* [out] */ MemoryBuf * pBuf)
{
    int error;
    struct ELA_IOVEC_TAG iovec1;
    struct ELA_UIO_TAG ioTag;

    if (pBuf == NULL) {
        return E_INVALID_ARGUMENT;
    }
    if (!pBuf->GetCapacity()) {
        return ErrorToECODE(EINVAL, "CJffs2File::Read");
    }

    if (offset > m_file.f_data->i_size \
        || toRead > SIZE_MAX || toRead > (UInt32)pBuf->GetCapacity()) {
        error = EINVAL;
        goto Exit;
    }

    error = IoOperator::jffs2_fo_lseek(&m_file, &offset, SEEK_SET);
    if (error != ENOERR)
        goto Exit;

    iovec1.iov_base = (char *)pBuf->GetPayload();
    iovec1.iov_len = toRead;

    ioTag.uio_iov = &iovec1;
    ioTag.uio_resid = toRead;
    ioTag.uio_iovcnt = 1;

    error = IoOperator::jffs2_fo_read(&m_file, &ioTag);
    if (error != ENOERR)
        goto Exit;

    pBuf->SetUsed(toRead - ioTag.uio_resid);

    return NOERROR;
Exit:
    return ErrorToECODE(error, "CJffs2File::Read");
}

ECode CJffs2File::Write(
                /* [in] */ UInt32 offset,
                /* [in] */ const MemoryBuf & ezbBuf,
                /* [out] */ UInt32 * pWrite)
{
    D2(printf("In CJffs2File::Write.\n"));

    int error;
    struct ELA_IOVEC_TAG iovec1;
    struct ELA_UIO_TAG ioTag;

    if (ezbBuf.IsNullOrEmpty() || NULL == pWrite) {
        return ErrorToECODE(EINVAL, "CJffs2File::Write");
    }

    if (!(m_file.f_flag & S_IWUSR)) {
        error = EPERM;
        goto Exit;
    }

    error = IoOperator::jffs2_fo_lseek(&m_file, &offset, SEEK_SET);
    if (error != ENOERR)
        goto Exit;

    iovec1.iov_base = (char *)ezbBuf.GetPayload();
    iovec1.iov_len = ezbBuf.GetUsed();

    ioTag.uio_iov = &iovec1;
    ioTag.uio_resid = iovec1.iov_len;
    ioTag.uio_iovcnt = 1;

    error = IoOperator::jffs2_fo_write(&m_file, &ioTag);
    if (error != ENOERR)
        goto Exit;
    assert(ioTag.uio_resid == 0);
    *pWrite = iovec1.iov_len;

    return NOERROR;
Exit:
    return ErrorToECODE(error, "CJffs2File::Write");
}

ECode CJffs2File::GetSize(
                /* [out] */ UInt32 * pSize)
{
    if (NULL == pSize) {
        return ErrorToECODE(EINVAL, "CJffs2File::GetSize");
    }

    *pSize = m_file.f_data->i_size;
    return NOERROR;
}

ECode CJffs2File::SetSize(
                /* [in] */ UInt32 size)
{
    int error;

    if (size == m_file.f_data->i_size)
        return NOERROR;

    if (!(m_file.f_flag & S_IWUSR)) {
        return ErrorToECODE(EPERM, "CJffs2File::SetSize");
    }

    if (size > m_file.f_data->i_size) {
        error = IoOperator::jffs2_fo_extend(&m_file, size);
        if (error != ENOERR)
            goto Exit;
    }
    else {
        error = IoOperator::jffs2_truncate_file(m_file.f_data, size);
        if (error != ENOERR)
            goto Exit;
    }
    return NOERROR;
Exit:
    return ErrorToECODE(error, "CJffs2File::SetSize");
}

ECode CJffs2File::GetOptimalBlockSize(
                /* [out] */ UInt32 * pSize)
{
    if (NULL == pSize) {
        return ErrorToECODE(EINVAL, "CJffs2File::GetOptimalBlockSize");
    }
    *pSize = PAGE_CACHE_SIZE;
    return NOERROR;
}

ECode CJffs2File::Sync()
{
    return NOERROR;
}

ECode CJffs2File::GetType(
                /* [out] */ enum FileType * pType)
{
    if (!pType)
        return ErrorToECODE(EINVAL, "CJffs2File::GetType");

    *pType = (enum FileType)m_file.f_type;
    return NOERROR;
}

ECode CJffs2File::GetAccessTime(
                /* [out] */ Int32 * pTime)
{
    int err;
    struct stat stat1;
    if (!pTime)
        return ErrorToECODE(EINVAL, "CJffs2File::GetAccessTime");

    err = IoOperator::jffs2_fo_fstat(&m_file, &stat1);
    if (err != ENOERR)
        return ErrorToECODE(err, "CJffs2File::GetAccessTime");

    *pTime = stat1.st_atime;
    return NOERROR;
}

ECode CJffs2File::SetAccessTime(
                /* [in] */ Int32 time)
{
    struct stat stat1;
    int err;

    if (time < SECONDSTO1980) {
        return ErrorToECODE(EINVAL, "CJffs2File::SetAccessTime");
    }

    if (!(m_file.f_flag & S_IWUSR)) {
        return ErrorToECODE(EPERM, "CJffs2File::SetAccessTime");
    }

    stat1.st_atime = time;
    stat1.st_mtime = m_file.f_data->i_mtime;
    stat1.st_mode = m_file.f_data->i_mode;

    err = IoOperator::jffs2_fo_set_fstat(&m_file, &stat1);
    if (err != ENOERR)
        return ErrorToECODE(err, "CJffs2File::SetAccessTime");
    return NOERROR;
}

ECode CJffs2File::GetModificationTime(
                /* [out] */ Int32 * pTime)
{
    int err;
    struct stat stat1;

    if (!pTime)
        return ErrorToECODE(EINVAL, "CJffs2File::GetModificationTime");

    err = IoOperator::jffs2_fo_fstat(&m_file, &stat1);
    if (err != ENOERR)
        return ErrorToECODE(err, "CJffs2File::GetModificationTime");

    *pTime = stat1.st_mtime;
    return NOERROR;
}

ECode CJffs2File::SetModificationTime(
                /* [in] */ Int32 time)
{
    struct stat stat1;
    int err;

    if (time < SECONDSTO1980) {
        return ErrorToECODE(EINVAL, "CJffs2File::SetModificationTime");
    }

    if (!(m_file.f_flag & S_IWUSR)) {
        return ErrorToECODE(EPERM, "CJffs2File::SetModificationTime");
    }

    stat1.st_atime = m_file.f_data->i_atime;
    stat1.st_mtime = time;
    stat1.st_mode = m_file.f_data->i_mode;

    err = IoOperator::jffs2_fo_set_fstat(&m_file, &stat1);
    if (err != ENOERR)
        return ErrorToECODE(err, "CJffs2File::SetModificationTime");
    return NOERROR;
}

ECode CJffs2File::GetCreateTime(
                /* [out] */ Int32 * pTime)
{
    int err;
    struct stat stat1;

    if (!pTime)
        return ErrorToECODE(EINVAL, "CJffs2File::GetCreateTime");

    err = IoOperator::jffs2_fo_fstat(&m_file, &stat1);
    if (err != ENOERR)
        return ErrorToECODE(err, "CJffs2File::GetCreateTime");

    *pTime = stat1.st_ctime;
    return NOERROR;
}

ECode CJffs2File::CheckAccessMode(
                /* [in] */ enum FileAccessMode mode)
{
    UInt32 nmode = m_file.f_flag;

    switch (mode) {
        case FileAccessMode_Read:
            if (nmode & S_IRUSR) {
                return NOERROR;
            }
            break;

        case FileAccessMode_Write:
            if (nmode & S_IWUSR) {
                return NOERROR;
            }
            break;

        case FileAccessMode_ReadWrite:
            if ((nmode & S_IRUSR) && (nmode & S_IWUSR)) {
                return NOERROR;
            }
            break;

        default:
            DPRINTF(("Unknown file mode = 0x%08x!\n", mode));
            return ErrorToECODE(EINVAL, "CJffs2File::CheckAccessMode");
    }
    return S_FALSE;
}

ECode CJffs2File::GetAccessMode(
                /* [out] */ enum FileAccessMode * pMode)
{
    UInt32 mode = m_file.f_flag;

    if (NULL == pMode) {
        return ErrorToECODE(EINVAL, "CJffs2File::GetAccessMode");
    }

    if ((mode & S_IRUSR) && (mode & S_IWUSR)) {
        *pMode = FileAccessMode_ReadWrite;
    }
    else if ((mode & S_IWUSR) && !(mode & S_IRUSR)) {
        *pMode = FileAccessMode_Write;
    }
    else {
        *pMode = FileAccessMode_Read;
    }

    return NOERROR;
}

ECode CJffs2File::SetAccessMode(
                /* [in] */ enum FileAccessMode mode)
{
    struct stat stat1;
    int err;

    UInt32 fmode = 0;

    switch (mode) {
        case FileAccessMode_Read:
            fmode = S_IRUSR;
            break;

        case FileAccessMode_Write:
            fmode = S_IWUSR;
            break;

        case FileAccessMode_ReadWrite:
            fmode = S_IRUSR | S_IWUSR;
            break;

        default:
            DPRINTF(("Unknown file mode = 0x%08x!\n", mode));
            return ErrorToECODE(EINVAL, "CJffs2File::SetAccessMode");
    }

    if (fmode == m_file.f_flag)
        return NOERROR;

    stat1.st_atime = m_file.f_data->i_atime;
    stat1.st_mtime = m_file.f_data->i_mtime;
    stat1.st_mode = m_file.f_data->i_mode & (~S_IRWXU) | fmode;

    err = IoOperator::jffs2_fo_set_fstat(&m_file, &stat1);
    if (err != ENOERR)
        return ErrorToECODE(err, "CJffs2File::SetAccessMode");
    return NOERROR;
}
#if 0
ECode CJffs2File::AddFileMonitor(
                /* [in] */ Boolean bManualReset,
                /* [in] */ UInt32 dwInitialState,
    /* [out] */ IEvent** pEvent)
{
    if (NULL == pEvent)
        return E_INVALID_ARGUMENT;

    CChangeNotifier *pNotifier = m_pCFatFile->pFatDir->p_Sharedata->pNotifier;

    if (NULL == pNotifier) {
        pNotifier = new CChangeNotifier;
        if (NULL == pNotifier)
            return E_OUT_OF_MEMORY;
    }

    *pEvent = pNotifier->AddMonitor(m_pCFatFile->f_node, bManualReset, dwInitialState);

    if (NULL == *pEvent)
        return E_FAIL;
    /* Make sure node who has been monitored could not be Release. */
    m_pCFatFile->pFatDir->ref_node(m_pCFatFile->f_node);
    m_pCFatFile->pFatDir->p_Sharedata->pNotifier = pNotifier;

    return NOERROR;
}

ECode CJffs2File::RemoveFileMonitor(
                /* [in] */ IEvent * pEvent)
{
    CChangeNotifier *pNotifier = m_pCFatFile->pFatDir->p_Sharedata->pNotifier;
    assert(pNotifier);

    if (pNotifier->RemoveMonitor(m_pCFatFile->f_node, pEvent))
        return E_FAIL;

    m_pCFatFile->pFatDir->deref_node(m_pCFatFile->f_node);

    return NOERROR;
}

inline void CJffs2File::Lock()
{
//    EnterCriticalSection(&this->m_pCFatFile->f_node->n_lock);
}

inline void CJffs2File::Unlock()
{
//    LeaveCriticalSection(&this->m_pCFatFile->f_node->n_lock);
}
#endif
